Why Follow Rules?
You can write your code any way that pleases you! The following rules are gathered from my own experience. Writing code this way makes it much easier to understand. It also makes it much easier to find and fix bugs. It also results in code that has fewer bugs to fret over!
Issue a "trapclose" command for every window that is opened in your program. If the user closes with window with the system menu or with the X button, your program is notified and you can put the proper "close" commands for all opened devices, such as files, windows or DLLs, in your exit routine.
Use the END command in the exit routine for your programs.
Indent one tab for each level of nesting in loops. Loops include "for...next", "while...wend" and "do...until/while". It is much easier to understand code that is written this way! Here is an example. The same code is written with indentation and without. Which is easier to read and understand?
'indented
for i = 1 to 10
for j = 30 to 35
print i + j
next
next
'
'
'no identation
for i = 1 to 10
for j = 30 to 35
print i + j
next
next
Indent one tab for each level of block if/then statements. Here is an example. The same code is written with indentation and without. Which is easier to read and understand?
'indented
if a = 2 then
if b > 17 then
print "Condition met."
else
print "B is too small!"
end if
else
print "A is not equal to 2."
end if
'
'
'no identation
if a = 2 then
if b > 17 then
print "Condition met."
else
print "B is too small!"
end if
else
print "A is not equal to 2."
end if
If a line of code scrolls off the edge of the page, it is much harder to understand. Use the line continuation character to break up long lines. Here is an API call on one line, and again with line breaks.
'one line version:
calldll #gdi32, "StretchBlt",hDCdest as long, x1 as long, y1 as long, nWidth1 as long, nHeight1 as long, hDCsource as long, x2 as long, y2 as long, nWidth2 as long, nHeight2 as long, _SRCPAINT as long, result as long
'with line breaks:
calldll #gdi32, "StretchBlt",hDCdest as long,_
x1 as long, y1 as long,_
nWidth1 as long, nHeight1 as long,_
hDCsource as long, x2 as long, y2 as long,_
nWidth2 as long, nHeight2 as long,_
_SRCPAINT as long, result as long
It is much easier to understand code as you are writing it, and later when you return to it, if you use descriptive variables and routine names. It is easier to debug and modify, and it is easier for others to understand. The use of 'i', 'j', and 'k' as counter variables is pretty standard. Other variables should be given names that indicate their use. It makes much more sense to call a variable "firstName$" than to call it "fn$" for instance. When you return to the code at a later time, you'll have no trouble at all understanding the purpose of the "firstName$" variable. The same is true for names of Subs, Functions and BranchLabels. [openFile] is self-explanatory. [op] may not be as clear! Although it is acceptable to use numbers as branch labels, the practice is discouraged. Look at this snip of code:
filedialog "Open","*.txt",file$ if file$ <>"" then [openFile]
Compare it with this one:
filedialog "Open","*.txt",f$ if file$ <>"" then [op]
Also, compare it with this one:
filedialog "Open","*.txt",f$ if file$ <>"" then goto 320
The first one is the clearest. Make things easy on yourself and use descriptive terms!
Isolate blocks of code that are used over and over. Put them into GOSUB routines, or SUBs or FUNCTIONs. This improves the readability of the program, making it much easier to see how it flows. In addition to making the code more readable, it saves typing the same lines again and again. It also makes it easy to lift valuable blocks of code and use them in other programs!
If speed of execution is an issue, it might be necessary to keep the routines inline, rather than calling them with GOSUB, SUB or FUNCTION calls. Use the method that works best for the task at hand.
It is easier to read code when loops and block if/then statements are indented. It is also much easier to read code when routines are separated by blank lines. Look at the two sample below to see the difference!
With blank lines:
Sub Subname num1,num2 print "Total is ";num1+num2 End Sub Function DoAdd(num1,num2,num3) DoAdd=num1+num2+num3 End Function [imaGosub] print "you are in a gosub" print "leaving gosub" return [aBranch] print "you are here" print "now you can wait" wait
No blank lines:
Sub Subname num1,num2 print "Total is ";num1+num2 End Sub Function DoAdd(num1,num2,num3) DoAdd=num1+num2+num3 End Function [imaGosub] print "you are in a gosub" print "leaving gosub" return [aBranch] print "you are here" print "now you can wait" wait
It is great to use descriptive variable names and branch labels to document code. Sometimes, that alone is not enough to make the purpose of the code clear. Don't be afraid to use comments! Use them liberally. Although the file size of the source code is larger because of the comments, they do not get compiled into the TKN version, so they do not add to the actual size of the completed program.
Place comments at the top of the code to explain the purpose of the code and include any copyright or author information.
Place comments throughout the code to document the purpose of routines, the meanings of variables, the reasons for conditional statements, and so on. Document the argument lists for subs and functions, too.
You can set up the Liberty BASIC editor to save copies of code in the BAK folder, or folder of your choice, whenever you run the code. If your program crashes and you haven't saved your code recently, you can recover your changes from the copy with BAK extension that was saved in your backup folder. This automatic backup will always overwrite the existing backup file. If you want to try something in the program, but you are not sure that it will work, or that you want to implement the new method, then save your code with a different version name. That way, you can go back to a previous version, and you won't have lost the original code. You might give files names that increment the version numbers, like this:
myprog01.bas
myprog02.bas
myprog03.bas
Or you might want to give the code descriptive names, like this:
myprogoriginal.bas
myprogNewGUI.bas
myprogAddMenus.bas
This one is very important! You can use the new Liberty BASIC command "on error goto", which sends your program to a specified routine when an error occurs. This prevents most program crashes, but remember that you must handle the error! Here is one example. What if your program attempts to open a nonexistent file to retrieve data? If you don't handle this problem somehow in the error-handling routine, then the program will continue, and it will assume that it has the necessary data. This could cause serious problems in the program!
Although it is great to have an "on error" feature, it is also good to trap errors at the source. Trap errors any time there is user input, and any time calculations are made. User input might be in the form of a filedialog. If the user cancels a file dialog, then no filename is returned. If your program tries to open a nonexistent file, you'll get an error. That problem is easily handled, like this:
filedialog "Open","*.txt",file$ if file$="" then wait open file$ for input as #f 'do stuff close #f
It is important to evaluate the results of calculations to prevent program crashes. A program will crash if it attempts to divide a number by zero. Trap that error like this:
if a <> 0 then b = c/a else b = c
There are many other kinds of input and calculation errors that could occur -- too many to list here! These two examples demonstrate what can happen, so be sure to include error handling code in your own programs.
If you are using loops to calculate and evaluate, pay attention to the code inside of each part of the loop. If you do a calculation each time in a loop that only needs to be done once at the start, the program will run more slowly. Here is a nonsense example:
'calculate outside the loop:
duh=val(duh$)
for i = 1 to 10
print i+duh
next
'instead of
for i = 1 to 10
print i + val(duh$)
next
In the second routine, the program must stop and provide a numeric value for "duh$" each time through the loop. In the first example, the value is determined just one time, so it will run faster. It is pretty easy to see how that works in a single loop, but it is even more important to watch for this in nested loops. Here is another example, where a value needs to be calculated in the outside loop, but needn't be calculated in the inside loop.
'the value of num only needs to
'be calculated in the outside loop:
for i = 1 to 50
num = i*2
for j = 1 to 20
print j + i
next
next
'oops, the value for num is calculated
'in the inside loop, which is not
'necessary - this will run slower!
for i = 1 to 50
for j = 1 to 20
num = i*2
print j + i
next
next